/*
 * Miscellaneous services
 *
 * Copyright 2004, Broadcom Corporation
 * All Rights Reserved.
 * 
 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
 *
 * $Id$
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

#include "rc.h"

extern int router_model;

static int start_telnetd(void)
{
	int ret;
	
	/* telnet is enabled by default */
	if (nvram_invmatch("telnet_enable", "1"))
		return 0;

	char *telnetd_argv[] = {"telnetd", NULL};
	pid_t pid;

	ret = _eval(telnetd_argv, NULL, 0, &pid);

	dprintf("done\n");
	return ret;
}

static int stop_telnetd(void)
{
	int ret = killall("telnetd");

	dprintf("done\n");
	return ret;
}

static int start_dropbear(void)
{
	char *dropbear_argv[] = {"dropbearstart", NULL, NULL, NULL, NULL, NULL};
	int i = 1;

	if (nvram_match("ssh_enable", "0"))
		return 0;

	if (nvram_match("ssh_password_logins", "0")) {
		dropbear_argv[i++] = "-s";
	}

	if (nvram_invmatch("ssh_port", "") && nvram_invmatch("ssh_port", "22"))
	{
		dropbear_argv[i++] = "-p";
		dropbear_argv[i++] = nvram_safe_get("ssh_port");
	}

#ifdef __CONFIG_IPV6__
	if (!nvram_invmatch("ipv6_proto", ""))
		dropbear_argv[i++] = "-4";
#endif

	int ret = _eval(dropbear_argv, NULL, 0, NULL);

	dprintf("done\n");
	return ret;
}

static int stop_dropbear(void)
{
	int ret = killall("dropbear");

	dprintf("done\n");
	return ret;
}

static int start_snmpd(void)
{
	const char *snmpd_conf = "/tmp/snmpd.conf";
	int ret;
	FILE *fp;
	
	if (!nvram_match("snmp_enable", "1"))
		return 0;
		
	/* create snmpd.conf  */
	if ((fp = fopen(snmpd_conf, "w")) == NULL) {
		perror(snmpd_conf);
		return 1;
	}

	fprintf(fp,
		"# automagically generated\n"
		"community %s\n"
		"syscontact %s\n"
		"syslocation %s\n",
		nvram_safe_default_get("snmp_community"),
		nvram_safe_default_get("snmp_contact"),
		nvram_safe_default_get("snmp_location"));
	fclose(fp);
	
	ret = eval("snmpd", "-c", snmpd_conf);

	dprintf("done\n");
	return ret;
}

int stop_snmpd(void)
{
	int ret = killall("snmpd");

	dprintf("done\n");
	return ret;
}

int start_httpd(void)
{
	int ret;

	chdir("/www");
	if (nvram_invmatch("router_disable", "1"))
		ret = eval("httpd", nvram_safe_get("wan0_ifname"));
	else
		ret = eval("httpd");

	chdir("/");

	dprintf("done\n");
	return ret;
}

static int stop_httpd(void)
{
	int ret = killall("httpd");

	dprintf("done\n");
	return ret;
}

int start_upnp(void)
{
	const char *wan_ifname;
	int ret;
	char var[100], prefix[sizeof("wanXXXXXXXXXX_")];
	FILE *fp;
	const char *lan_addr, *lan_mask, *lan_url;
	char lan_class[32];
	uint8_t lan_mac[16];
	char name[32], friendly_name[64];
	
	int upnp_enable = nvram_get_int("upnp_enable");

	if (!upnp_enable || nvram_match("router_disable", "1"))
		return 0;

	ret = killall_s("miniupnpd", SIGUSR1);
	if (ret != 0)
	{
		snprintf(prefix, sizeof(prefix), "wan%d_", wan_primary_ifunit());
		switch (_wan_proto(prefix, var)) {
		case WAN_PPPOE:
		case WAN_PPTP:
		case WAN_L2TP:
			wan_ifname = nvram_safe_get(strcat_r(prefix, "pppoe_ifname", var));
			break;
#ifdef __CONFIG_MADWIMAX__
		case WAN_WIMAX:
			wan_ifname = nvram_safe_get(strcat_r(prefix, "wimax_ifname", var));
			break;
#endif
		default:
			wan_ifname = nvram_safe_get(strcat_r(prefix, "ifname", var));
			break;
		}

		lan_addr = nvram_safe_get("lan_ipaddr");
		lan_mask = nvram_safe_get("lan_netmask");
		ip2class(lan_addr, lan_mask, lan_class);
		memset(lan_mac, 0, sizeof(lan_mac));
		ether_atoe(nvram_safe_get("lan_hwaddr"), lan_mac);

		lan_url = lan_addr;
		ret = nvram_get_int("http_lanport");
		if (ret && ret != 80) {
			sprintf(var, "%s:%d", lan_addr, ret);
			lan_url = var;
		}

		get_model_t(friendly_name, sizeof(friendly_name), GET_MODEL_FULL);
		get_model_t(name, sizeof(name), GET_MODEL_NAME);

		/* Touch leases file */
		if (!(fp = fopen("/tmp/upnp.leases", "a"))) {
			perror("/tmp/upnp.leases");
			return errno;
		}
		fclose(fp);

		/* Write configuration file */
		if (!(fp = fopen("/etc/miniupnpd.conf", "w"))) {
			perror("/etc/miniupnpd.conf");
			return errno;
		}

		/* General settings */
		fprintf(fp, "# automagically generated\n"
			"uuid=%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x00000000\n"
			"friendly_name=%s\n"
			"model_number=%s\n"
			"presentation_url=http://%s/\n"
			"ext_ifname=%s\n"
			"listening_ip=%s/%s\n"
			"listening_ip=127.0.0.1/8\n"
			"port=0\n"
			"enable_upnp=%s\n"
			"enable_natpmp=%s\n"
			"lease_file=/tmp/upnp.leases\n"
			"system_uptime=yes\n",
			lan_mac[0], lan_mac[1], lan_mac[2], lan_mac[3], lan_mac[4], lan_mac[5],
			lan_mac[0], lan_mac[1], lan_mac[2], lan_mac[3], lan_mac[4], lan_mac[5],
			friendly_name,
			name,
			lan_url,
			wan_ifname,
			lan_addr, lan_mask,
			(upnp_enable & 1) ? "yes" : "no",
			(upnp_enable & 2) ? "yes" : "no");

		/* Time-dependent settings */
		fprintf(fp,
			"notify_interval=%u\n"
			"clean_ruleset_interval=%u\n",
			nvram_get_int("upnp_notify") ? : 60,
			nvram_get_int("upnp_clean_ruleset") ? : 600);

		/* Security settings */
		fprintf(fp,
			"secure_mode=%s\n"
			"allow %u-65535 %s %u-65535\n"
			"deny 0-65535 0.0.0.0/0 0-65535\n",
			nvram_get_int("upnp_secure_mode") ? "yes" : "no",
			nvram_get_int("upnp_min_port") ? : 1024,
			lan_class,
			nvram_get_int("upnp_min_port") ? : 1024);

		fclose(fp);

		ret = eval("miniupnpd");
	}
	dprintf("done\n");
	return ret;
}

int
stop_upnp(void)
{
	int ret = 0;

	if (nvram_invmatch("upnp_enable", "0"))
		ret = killall_w("miniupnpd", 0, 1);

	dprintf("done\n");
	return ret;
}

int start_nas(const char *type)
{
	if ((nvram_match("wl0_radio", "0")) || (nvram_match("security_mode", "disabled")))
                return 1;

#ifdef __CONFIG_BCMWL5__
	setenv("UDP_BIND_IP", "127.0.0.1", 1);
        eval("eapd");
        unsetenv("UDP_BIND_IP");
        usleep(100000);
        eval("nas");
#else /* __CONFIG_BCMWL5__ */
	char cfgfile[64];
	char pidfile[64];
	pid_t pid;

	if (!type || !*type)
		type = "lan";

	snprintf(cfgfile, sizeof(cfgfile), "/tmp/nas.%s.conf", type);
	snprintf(pidfile, sizeof(pidfile), "/tmp/nas.%s.pid", type);
	{
		char *nas_argv[] = {"nas", cfgfile, pidfile, type, NULL};

		_eval(nas_argv, NULL, 0, &pid);
	}
#endif /* !__CONFIG_BCMWL5__ */
	dprintf("done\n");
	return 0;
}

int
stop_nas(void)
{
	int ret = killall("nas");

#ifdef __CONFIG_BCMWL5__
        killall("eapd");
#endif
	dprintf("done\n");
	return ret;
}

int
start_ntpc(void)
{
	pid_t pid = 0;
	char *server0 = nvram_safe_get("ntp_server0"), *server1;
	char *ntp_argv[] = {"ntpd", "-qt", "-p", server0, NULL, NULL, NULL};
	FILE *fp;

	if (strlen(server0) > 0) {
		server1 = nvram_safe_get("ntp_server1");
		if (strlen(server1) > 0) {
			ntp_argv[4] = "-p";
			ntp_argv[5] = server1;
		}
		_eval(ntp_argv, NULL, 0, &pid);

		/* write pid */
		if (pid && (fp = fopen("/var/run/ntpc.pid", "w")) != NULL)
		{
			fprintf(fp, "%d", pid);
			fclose(fp);
		}
	}

	return pid;
}

int
stop_ntpc(void)
{
	int ret = kill_pidfile("/var/run/ntpc.pid");
	if (ret == 0)
		unlink("/var/run/ntpc.pid");

	return ret;
}

int
start_services(void)
{
	start_logger();
	start_nas("lan");
	start_wl(); // Start WLAN
	start_telnetd();
	start_dropbear();
	start_httpd();
	start_dns();
	start_dhcpd();
#ifdef __CONFIG_RADVD__
	start_radvd();
#endif
	start_snmpd();
	start_upnp();
	start_lltd();
	start_usb();

	dprintf("done\n");
	return 0;
}

int
stop_services(void)
{
	preshutdown_system();

	stop_usb();
	stop_nas();
	stop_upnp();
	stop_snmpd();
#ifdef __CONFIG_RADVD__
	stop_radvd();
#endif
#ifdef __CONFIG_IPV6__
	stop_dhcp6c();
#endif
	stop_dhcpd();
	stop_dns();
	stop_httpd();
	stop_dropbear();
	stop_telnetd();
	stop_logger();
	dprintf("done\n");
	return 0;
}
